﻿using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;

#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8603 // Possible null reference return.

namespace Hsenl {
    // 线程安全
    internal sealed class HTaskCompletionBody : HTaskBody {
        private const int StatePending = 0;
        private const int StateCompleted = 1;
        private const int StateAborted = 2;
        private const int StateFaulted = 3;

        private int _status = StatePending;
        private Action? _continuation;
        private ExceptionDispatchInfo? _exception;
        private readonly object _locker = new();
        private readonly ManualResetEventSlim _completionEvent = new(false);

        public override HTaskStatus Status {
            get {
                return this._status switch {
                    StatePending => HTaskStatus.Pending,
                    StateCompleted => HTaskStatus.Succeeded,
                    StateAborted => HTaskStatus.Aborted,
                    StateFaulted => HTaskStatus.Faulted,
                    _ => throw new InvalidOperationException("Invalid status code")
                };
            }
        }

        public override bool IsCompleted => this._status != StatePending;

        public override void GetResult() {
            // 等待任务完成
            if (!this._completionEvent.IsSet) {
                this._completionEvent.Wait();
            }

            try {
                if (this._exception != null) {
                    // 说明真的有异常, 那就正常抛给上层
                    var e = this._exception;
                    this._exception = null;
                    e.Throw();
                }
                else if (this._status == StateAborted) {
                    // 没有异常, 但是因为被外部调用终止了, 所以抛出一个终止标记给上一层
                    throw HTaskAborter.Aborter;
                }
            }
            finally {
                this.ReturnBody();
            }
        }

        public override void SetResult() {
            Action? continuation;

            lock (this._locker) {
                if (this._status != StatePending) {
                    throw new InvalidOperationException("Task is already completed");
                }

                this.IncrementVersion();

                if (Interlocked.CompareExchange(ref this._status, StateCompleted, StatePending) != StatePending) {
                    throw new InvalidOperationException("Task status changed concurrently");
                }

                continuation = this._continuation;
                this._continuation = null;
            }

            // 在锁外部调用continuation, 避免死锁
            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void SetException(Exception e) {
            Action? continuation;

            lock (this._locker) {
                if (this._status != StatePending) {
                    throw new InvalidOperationException("Task is already completed");
                }

                this.IncrementVersion();
                var newState = e is OperationCanceledException ? StateAborted : StateFaulted;

                if (Interlocked.CompareExchange(ref this._status, newState, StatePending) != StatePending) {
                    throw new InvalidOperationException("Task status changed concurrently");
                }

                continuation = this._continuation;
                this._continuation = null;

                if (continuation != null) {
                    this._exception = ExceptionDispatchInfo.Capture(e);
                }
                else {
                    // 例如下面这种情况
                    // async HTask Fun(){}
                    // void Test()
                    // {
                    //    Fun().Tial();
                    // }
                    // 我们拿到Fun返回的task, 但却没有继续await他, 这就导致了这里的this._continuation是空的, 而由此也导致不会触发上面的GetResult(), 也就导致了
                    // exception不会传下去, 也就导致了"异常被吞了"的情况. 面对这种情况, 我们直接在这里就把异常抛出去.
                    this.ReturnBody();
                    if (newState == StateFaulted)
                        ExceptionDispatchInfo.Capture(e).Throw();
                }
            }

            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void Abort() {
            Action? continuation = null;

            lock (this._locker) {
                if (this._status != StatePending) {
                    return; // 已经完成, 忽略
                }

                this.IncrementVersion();

                if (Interlocked.CompareExchange(ref this._status, StateAborted, StatePending) == StatePending) {
                    continuation = this._continuation;
                    this._continuation = null;
                }

                if (continuation == null) {
                    this.ReturnBody();
                }
            }

            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void OnCompleted(Action continuation) {
            if (continuation == null) throw new ArgumentNullException(nameof(continuation));

            var runImmediately = false;

            lock (this._locker) {
                if (this._status == StatePending) {
                    Interlocked.Exchange(ref this._continuation, continuation);
                }
                else {
                    runImmediately = true;
                }
            }

            // 如果任务已经完成, 立即执行continuation
            if (runImmediately) {
                continuation.Invoke();
            }
        }

        public override void UnsafeOnCompleted(Action continuation) {
            this.OnCompleted(continuation);
        }

        public override void Dispose() {
            lock (this._locker) {
                if (this.IsDisposed)
                    return;

                base.Dispose();

                this._status = StatePending;
                this._continuation = null;
                this._exception = null;

                try {
                    this._completionEvent.Reset();
                }
                catch {
                    // 忽略清理异常
                }
            }
        }
    }

    internal sealed class HTaskCompletionBody<T> : HTaskBody<T> {
        private const int StatePending = 0;
        private const int StateCompleted = 1;
        private const int StateAborted = 2;
        private const int StateFaulted = 3;

        private int _status = StatePending;
        private Action? _continuation;
        private ExceptionDispatchInfo? _exception;
        private T _value;
        private readonly object _locker = new();
        private readonly ManualResetEventSlim _completionEvent = new(false);

        public override HTaskStatus Status {
            get {
                return this._status switch {
                    StatePending => HTaskStatus.Pending,
                    StateCompleted => HTaskStatus.Succeeded,
                    StateAborted => HTaskStatus.Aborted,
                    StateFaulted => HTaskStatus.Faulted,
                    _ => throw new InvalidOperationException("Invalid status code")
                };
            }
        }

        public override bool IsCompleted => this._status != StatePending;

        public override T GetResult() {
            // 等待任务完成
            if (!this._completionEvent.IsSet) {
                this._completionEvent.Wait();
            }

            try {
                var v = this._value;
                this._value = default!;

                if (this._exception != null) {
                    // 说明真的有异常, 那就正常抛给上层
                    var e = this._exception;
                    this._exception = null;
                    e.Throw();
                }
                else if (this._status == StateAborted) {
                    // 没有异常, 但是因为被外部调用终止了, 所以抛出一个终止标记给上一层
                    throw HTaskAborter.Aborter;
                }

                return v;
            }
            finally {
                this.ReturnBody();
            }
        }

        public override void SetResult(T value) {
            Action? continuation;

            lock (this._locker) {
                if (this._status != StatePending) {
                    throw new InvalidOperationException("Task is already completed");
                }

                this.IncrementVersion();
                this._value = value;

                if (Interlocked.CompareExchange(ref this._status, StateCompleted, StatePending) != StatePending) {
                    throw new InvalidOperationException("Task status changed concurrently");
                }

                continuation = this._continuation;
                this._continuation = null;
            }

            // 在锁外部调用continuation, 避免死锁
            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void SetException(Exception e) {
            Action? continuation;

            lock (this._locker) {
                if (this._status != StatePending) {
                    throw new InvalidOperationException("Task is already completed");
                }

                this.IncrementVersion();
                var newState = e is OperationCanceledException ? StateAborted : StateFaulted;

                if (Interlocked.CompareExchange(ref this._status, newState, StatePending) != StatePending) {
                    throw new InvalidOperationException("Task status changed concurrently");
                }

                continuation = this._continuation;
                this._continuation = null;

                if (continuation != null) {
                    this._exception = ExceptionDispatchInfo.Capture(e);
                }
                else {
                    // 例如下面这种情况
                    // async HTask Fun(){}
                    // void Test()
                    // {
                    //    Fun().Tial();
                    // }
                    // 我们拿到Fun返回的task, 但却没有继续await他, 这就导致了这里的this._continuation是空的, 而由此也导致不会触发上面的GetResult(), 也就导致了
                    // exception不会传下去, 也就导致了"异常被吞了"的情况. 面对这种情况, 我们直接在这里就把异常抛出去.
                    this.ReturnBody();
                    if (newState == StateFaulted)
                        ExceptionDispatchInfo.Capture(e).Throw();
                }
            }

            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void Abort() {
            Action? continuation = null;

            lock (this._locker) {
                if (this._status != StatePending) {
                    return; // 已经完成, 忽略
                }

                this.IncrementVersion();

                if (Interlocked.CompareExchange(ref this._status, StateAborted, StatePending) == StatePending) {
                    continuation = this._continuation;
                    this._continuation = null;
                }

                if (continuation == null) {
                    this.ReturnBody();
                }
            }

            // 在锁外部调用continuation
            this._completionEvent.Set();
            continuation?.Invoke();
        }

        public override void OnCompleted(Action continuation) {
            if (continuation == null) throw new ArgumentNullException(nameof(continuation));

            var runImmediately = false;

            lock (this._locker) {
                if (this._status == StatePending) {
                    Interlocked.Exchange(ref this._continuation, continuation);
                }
                else {
                    runImmediately = true;
                }
            }

            // 如果任务已经完成, 立即执行continuation
            if (runImmediately) {
                continuation.Invoke();
            }
        }

        public override void UnsafeOnCompleted(Action continuation) {
            this.OnCompleted(continuation);
        }

        public override void Dispose() {
            lock (this._locker) {
                if (this.IsDisposed)
                    return;

                base.Dispose();

                this._status = StatePending;
                this._continuation = null;
                this._exception = null;
                this._value = default!;

                try {
                    this._completionEvent.Reset();
                }
                catch {
                    //
                }
            }
        }
    }
}